/*
 * ID: mygpio.c
 */
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/ioctl.h>
#include "ioctl_nr.h"
#include <linux/timer.h>
#include <linux/jiffies.h>

#define MAXLEN       512

static struct cdev MyGpioDev;
struct file_operations mygpio_Fops;
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
struct timer_list my_timer;

static dev_t devno;
static char* devlabel = "mygpio";
static unsigned int timeout_in_sec = 1;

struct gpioPort{
  unsigned int num;
  enum direnu { in, out } dir;
  const char label[10]; // Very important to specify a size. Just [] causes havoc to gpio indexing
  int irq;
} gpio[] = {
	{130, in, "gpio1_0", 0},
	{131, in, "gpio1_1", 0},
	{132, in, "gpio1_2", 0},
	{133, in, "gpio1_3", 0},
	{134, out, "gpio1_4", 0},
	{135, out, "gpio1_5", 0},
	{136, out, "gpio1_6", 0},
	{137, out, "gpio1_7", 0},
	{139, out, "led3", 0}
};

const unsigned int gpio_len = sizeof(gpio) / 22; // num (4) + direnu (4) + label (10) + irq (4) = 22

bool check_minor(int minor)
{
    if (minor >= 0 && minor < gpio_len)
        return true;
    printk(KERN_ALERT "Error: minor number is invalid\n");
    return false;
}

static irqreturn_t irq_handler(int irq, void *dummy, struct pt_regs * regs)
{
    //printk(KERN_DEBUG "Interrupt handler executed for IRQ: %i\n", irq);
    flag = 1;
    wake_up_interruptible(&wq);
    return IRQ_HANDLED;
}

static int mygpio_init(void)
{
    int err, i;
    char insmod_text[50];
    strcpy(insmod_text, devlabel);
    printk(KERN_ALERT "%s\n", strcat(insmod_text, " Module Inserted"));

    /*
     * Allocate Major/Minor Numbers 
     */
    if(alloc_chrdev_region(&devno, 0, gpio_len, devlabel) < 0) {
      printk(KERN_ALERT "Can't Register device\n");
      return -1;
    }

    /*
     * Create cdev
     */       
    cdev_init(&MyGpioDev, &mygpio_Fops);
    MyGpioDev.owner = THIS_MODULE;
    MyGpioDev.ops   = &mygpio_Fops;
    err             = cdev_add(&MyGpioDev, devno, gpio_len);
    if (err) {
        printk (KERN_ALERT "Error %d adding %s device\n", err, devlabel);
        return -1;
    }

    /*
     * Request GPIO Ressources
     */
    for (i=0; i<gpio_len; ++i)
        if ((err = gpio_request(gpio[i].num, gpio[i].label)) < 0)
        {
            printk (KERN_ALERT "Error %d requesting %s ressource\n", err, gpio[i].label);
            return err;
        }
    /*
     * Set GPIO Direction
     */
    for (i=0; i<gpio_len; ++i)
    {
        if (gpio[i].dir == in)
        {
	    if ((err = gpio_direction_input(gpio[i].num)) < 0)
	    {
	        printk (KERN_ALERT "Error %d setting %s as input\n", err, gpio[i].label);
                return err;
	    }
        }
        else
        { 
            if ((err = gpio_direction_output(gpio[i].num, 0)) < 0)
	    {
	        printk (KERN_ALERT "Error %d setting %s as output\n", err, gpio[i].label);
                return err;
	    }
        }
    }

    /*
     * Get IRQ number mapped to GPIO
     */
    for (i=0; i<gpio_len; ++i)
        if (gpio[i].dir == in) // only get IRQ number for input pins
        {
            if ((gpio[i].irq = gpio_to_irq(gpio[i].num)) < 0)
            {
                printk (KERN_ALERT "Error mapping GPIO %s to IRQ\n", gpio[i].label);
                return gpio[i].irq;
            } else
                printk(KERN_NOTICE "GPIO %s assigned to IRQ %i\n", gpio[i].label, gpio[i].irq);
        }

    /*
     * Request IRQ Ressources
     */
    for (i=0; i<gpio_len; ++i)
        if (gpio[i].irq > 0) // if gpio is input IRQ will be != 0 (set by above loop)
            if ((err = request_irq(gpio[i].irq, irq_handler, IRQF_TRIGGER_RISING, gpio[i].label, NULL)) < 0)
            {
                printk (KERN_ALERT "Error requesting irq for %i\n", gpio[i].irq);
                return err;
            }

    return 0;
}

static void mygpio_exit(void)
{
  int i, err;

    printk(KERN_NOTICE "Removing %s Module\n", devlabel);

    /*
     * Set GPIOs to Inputs 
     * (leave things as when we entered)
     */
    for (i=0; i<gpio_len; ++i)
	if ((err = gpio_direction_input(gpio[i].num)) < 0)
	    printk (KERN_ALERT "Error %d setting %s as input\n", err, gpio[i].label);


    /*
     * Free GPIO Ressources 
     */
    for (i=0; i<gpio_len; ++i)
    	gpio_free(gpio[i].num);


    /*
     * Delete cdev
     */
    unregister_chrdev_region(devno, gpio_len);
    cdev_del(&MyGpioDev);

    /*
     * Free IRQ Ressources 
     */
    for (i=0; i<gpio_len; ++i)
        free_irq(gpio[i].irq, NULL);
}


int mygpio_open(struct inode *inode, struct file *filep)
{
    int major, minor;

    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);

    printk("Opening %s Device [major], [minor], [gpio], [direction], [label]: %i, %i, %i, %s, %s\n", devlabel, major, minor, gpio[minor].num, gpio[minor].dir == in ? "IN" : "OUT", gpio[minor].label);


    /*
     * Check if major/minor is valid
     */
    if (!check_minor(minor)) return -1;

    /*
     *  Try to get the module sem
     */
    if (!try_module_get(mygpio_Fops.owner)) // Get Module
      return -ENODEV;

    return 0;
}

int mygpio_release(struct inode *inode, struct file *filep)
{
    int minor, major;
    
    major = MAJOR(inode->i_rdev);
    minor = MINOR(inode->i_rdev);

    printk("Closing %s Device [major], [minor]: %i, %i\n", devlabel, major, minor);


    /*
     * Check if major/minor is valid
     */
    if (!check_minor(minor)) return -1;


    /*
     * Put the module semaphore
     * to release it
     */
    module_put(mygpio_Fops.owner);
    
    return 0;
}



ssize_t mygpio_write(struct file *filep, const char __user *ubuf, 
                  size_t count, loff_t *f_pos)
{
    int minor, len, value;
    char kbuf[MAXLEN];
    
    /*
     * retrieve minor from file ptr
     */
    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    printk(KERN_ALERT "Writing to %s [Minor] %i\n", devlabel, minor);

    /*
     * Check if minor is valid
     */
    if (!check_minor(minor)) return -1;

    /*
     * Copy data from user space
     * to kernel space
     */  
    len = count < MAXLEN ? count : MAXLEN; // set len < MAXLEN

    /**** Use copy_from_user to get data ****/
    copy_from_user(kbuf, ubuf, len);
	
    kbuf[len] = '\0';   // Pad null termination to string
    printk("string from user: %s\n", kbuf);

    /*
     * Convert string to int
     */

    /**** Use sscanf to convert string to int ****/
    sscanf(kbuf,"%i", &value);
    printk("value %i\n", value);
    
    value = value > 0 ? 1 : 0;

    /*
     * Set GPIO port
     * (if an output port)
     */

    /**** Use gpio_set_value on appropriate port ****/
    if (gpio[minor].dir == out)
        gpio_set_value(gpio[minor].num, value);
    else
    {
        printk (KERN_ALERT "Error trying to write to none output on %s\n", gpio[minor].label);
        return -1;
    }
	
    *f_pos += count;
    return count;
}

ssize_t mygpio_read(struct file *filep, char __user *buf, 
                 size_t count, loff_t *f_pos)
{
   char readBuf[MAXLEN];
   int len, result=-1, minor;

    /*
     * retrieve minor from file ptr
     */
    minor = MINOR(filep->f_dentry->d_inode->i_rdev);
    printk(KERN_ALERT "Reading from %s [Minor] %i\n", devlabel, minor);

    /*
     * Check if minor is valid
     */
    if (!check_minor(minor)) return -1;

    /*
     * Sleep until input changes
     * (where appropriate)
     */
    if (gpio[minor].irq > 0)
    {
        //printk(KERN_DEBUG "process %i (%s) going to sleep\n",current->pid, current->comm);
        wait_event_interruptible(wq, flag != 0);
        flag = 0;
        //printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
    }

    /*
     * Read GPIO port
     * (if an input port)
     */
    if (gpio[minor].dir == in)
        result = gpio_get_value(gpio[minor].num);
    else
    {
        printk (KERN_ALERT "Error trying to read none input %s\n", gpio[minor].label);
	return -1;
    }

    /*
     * Convert int to string
     * using sprintf
     */
    len=sprintf(readBuf,"%i", result);
    len++; // To include null-termination

    /*
     * Copy string result
     * to User Space
     */
    copy_to_user(buf, readBuf, len);

    *f_pos += len;
    return len;    
}

static void timer_funct(unsigned long funct_parameter) {
  // Re-schedule the timer
  my_timer.expires = jiffies + timeout_in_sec * HZ;
  my_timer.data = funct_parameter;
  my_timer.function = timer_funct;
  add_timer(&my_timer);
  gpio_set_value(gpio[8].num, !gpio_get_value(gpio[8].num));
}

ssize_t mygpio_ioctl(struct inode *inode, struct file *filep,
                           unsigned int cmd, unsigned long arg)
{
	int minor = MINOR(filep->f_dentry->d_inode->i_rdev);
	switch (cmd)
	{
	case START_TIMER:
		printk(KERN_NOTICE "Received ioctl-command: %d (START_TIMER)\n", cmd);
		init_timer(&my_timer);
		my_timer.expires = jiffies + timeout_in_sec * HZ;
		my_timer.data = minor;
		my_timer.function = timer_funct;
		add_timer(&my_timer);
		break;
	case STOP_TIMER:
		printk(KERN_NOTICE "Received ioctl-command: %d (STOP_TIMER)\n", cmd);
		del_timer(&my_timer);
		break;
	case SET_FREQUENCY:
		printk(KERN_NOTICE "Received ioctl-command: %d (SET_FREQUENCY)\n", cmd);
		timeout_in_sec = arg;
		break;
	default:
		printk(KERN_ALERT "Unknown ioctl-command: %d \n", cmd);
		break;
	}
	return 0;
}

struct file_operations mygpio_Fops = 
{
    .owner   = THIS_MODULE,
    .open    = mygpio_open,
    .release = mygpio_release,
    .write   = mygpio_write,
    .read    = mygpio_read,
    .ioctl   = mygpio_ioctl,
};

module_init(mygpio_init);
module_exit(mygpio_exit);

MODULE_DESCRIPTION("TEST Module");
MODULE_AUTHOR("IHA <http://www.iha.dk>");
MODULE_LICENSE("GPL");
